<?php
/**
 * @link https://www.kancloud.cn/cleverstone/ymb2
 * @copyright Copyright (c) 2020 Yii Manager Software LLC
 */

namespace builder\lists;

use Yii;
use yii\web\View;
use yii\web\AssetBundle;
use yii\base\InvalidArgumentException;
use builder\helper\H5;
use builder\helper\RegEx;
use builder\base\Builder;
use builder\lists\util\lrows\Item;
use builder\base\ViewBuilderInterface;
use builder\helper\App;

/**
 * 列表构建器
 * @property bool $partial 是否是局部继承
 * @property string $title 列表标题
 * @property array $rootLayout 根布局
 * @property array $listLayout 列表布局
 * @property array $listRows 列表行数据
 * @property array $extraJs 设置额外的Js
 * @property array $extraCss 设置额外的Css
 * @property array $extraAssetBundle 设置额外的静态资源包
 * @author cleverstone
 * @since ym2.0
 */
class ListsBuilder extends Builder implements ViewBuilderInterface
{
    // 头部切点
    const POS_HEAD = 0x01;

    // 尾部切点
    const POS_END = 0x02;

    /**
     * @var array 切点
     * @see registerPointcut()
     * @see beginPage()
     * @see endPage()
     */
    private $_pointcutBody = [];

    /**
     * @var bool 是否局部继承
     * @see $partial
     * @see setPartial()
     * @see getPartial()
     */
    private $_partial = true;

    /**
     * @var string 列表标题
     * @see $title
     * @see setTitle()
     * @see getTitle()
     */
    private $_title;

    /**
     * @var array 根布局
     * @see $rootLayout
     * @see setRootLayout()
     * @see getRootLayout()
     */
    private $_rootLayout = [];

    /**
     * @var array 列表布局
     * @see $listLayout
     * @see setListLayout()
     * @see getListLayout()
     */
    private $_listLayout = [];

    /**
     * @var array 列表行数据
     * @see $listRows
     * @see setListRows()
     * @see getListRows()
     */
    private $_listRows = [];

    /**
     * @var array 额外的JS
     * ```php
     * $lists->extraCss = <<<JS
     * console.log("测试一下 [setExtraCss] 方法;")
     * JS;
     *
     * ```
     * @see $extraJs
     * @see setExtraJs()
     * @see getExtraJs()
     */
    private $_extraJs = [];

    /**
     * @var array 额外的CSS
     * ```php
     * $lists->extraCss = <<<CSS
     * body{background-color: #fff;}
     * CSS;
     *
     * ```
     * @see $extraCss
     * @see setExtraCss()
     * @see getExtraCss()
     */
    private $_extraCss = [];

    /**
     * @var array 额外的静态资源包
     * ```php
     * $lists->extraAssetBundle = MemberAsset::class;
     *
     * ```
     * @see $extraAssetBundle
     * @see setExtraAssetBundle()
     * @see getExtraAssetBundle()
     */
    private $_extraAssetBundle = [];

    /**
     * 实例化
     * @param array $config
     * @return $this
     */
    public static function instance($config = [])
    {
        return new static($config);
    }

    /**
     * 注册切点
     * @param string $content 注册的内容
     * @param int $pos 切点标记
     * @return $this
     */
    public function registerPointcut($content, $pos = self::POS_HEAD)
    {
        $this->_pointcutBody[$pos][] = $content;
        return $this;
    }

    /**
     * 页面头部位置
     * @return null|string
     */
    public function beginPage()
    {
        if (empty($this->_pointcutBody[self::POS_HEAD])) {
            return null;
        }

        return implode("\n", $this->_pointcutBody[self::POS_HEAD]);
    }

    /**
     * 页面尾部位置
     * @return null|string
     */
    public function endPage()
    {
        if (empty($this->_pointcutBody[self::POS_END])) {
            return null;
        }

        return implode("\n", $this->_pointcutBody[self::POS_END]);
    }

    /**
     * 设置视图局部继承
     * @param bool $boolean
     * @return $this
     */
    public function setPartial($boolean = true)
    {
        $this->_partial = $boolean;
        return $this;
    }

    /**
     * @return bool
     */
    public function getPartial()
    {
        return $this->_partial;
    }

    /**
     * 设置列表标题
     * @param string $title
     * @return $this
     */
    public function setTitle($title)
    {
        $this->_title = (string)$title;
        return $this;
    }

    /**
     * @return string
     */
    public function getTitle()
    {
        return $this->_title;
    }

    /**
     * 设置根布局
     * @param array $options
     * @return $this
     */
    public function setRootLayout(array $options)
    {
        if (!empty($options['class'])) {
            $this->formatUiClass($options['class']);
        } else {
            $options['class'] = '';
        }

        if (!empty($options['style'])) {
            $this->formatStyle($options['style']);
        } else {
            $options['style'] = '';
        }

        if (!empty($options['attribute'])) {
            $this->formatHtmlAttribute($options['attribute']);
        } else {
            $options['attribute'] = '';
        }

        $this->_rootLayout = $options;
        return $this;
    }

    /**
     * @return array
     */
    public function getRootLayout()
    {
        if (empty($this->_rootLayout)) {
            return [
                'style' => '',
                'class' => '',
                'attribute' => '',
            ];
        }

        return $this->_rootLayout;
    }

    /**
     * 设置列表布局
     * @param array $options
     * ```php
     * // 改变表格皮肤
     * $this->setListLayout([
     *      'attribute' => ['lay-skin' => 'nob']
     * ])
     * ```
     * @return $this
     */
    public function setListLayout(array $options)
    {
        if (!empty($options['class'])) {
            $this->formatUiClass($options['class']);
        } else {
            $options['class'] = '';
        }

        if (!empty($options['style'])) {
            $this->formatStyle($options['style']);
        } else {
            $options['style'] = '';
        }

        if (!empty($options['attribute'])) {
            $this->formatHtmlAttribute($options['attribute']);
        } else {
            $options['attribute'] = '';
        }

        $this->_listLayout = $options;
        return $this;
    }

    /**
     * @return array
     */
    public function getListLayout()
    {
        if (empty($this->_listLayout)) {
            return [
                'style' => '',
                'class' => '',
                'attribute' => '',
            ];
        }

        return $this->_listLayout;
    }

    /**
     * 列表行数据递归过滤
     * @param array $options
     * @return array
     */
    private function _listRowsRecursiveMap(array $options)
    {
        foreach ($options as $label => &$item) {
            if (is_int($label) && is_array($item)) {
                // 递归处理子项
                $item = $this->_listRowsRecursiveMap($item);
            } else {
                // 正常项
                if (is_callable($item)) {
                    $item = call_user_func($item);
                }

                if ($item instanceof Item) {
                    $item = $item->_toArray();
                }

                $this->throwExceptionIfNotArray($item);

                // HTML处理
                if (!empty($item['thStyle'])) {
                    $this->formatStyle($item['thStyle']);
                }

                if (!empty($item['thClass'])) {
                    $this->formatUiClass($item['thClass']);
                }

                if (!empty($item['thAttribute'])) {
                    $this->formatHtmlAttribute($item['thAttribute']);
                }

                if (!empty($item['tdStyle'])) {
                    $this->formatStyle($item['tdStyle']);
                }

                if (!empty($item['tdClass'])) {
                    $this->formatUiClass($item['tdClass']);
                }

                if (!empty($item['tdAttribute'])) {
                    $this->formatHtmlAttribute($item['tdAttribute']);
                }

                // 内容过滤
                if (!empty($item['content'])) {
                    if (is_callable($item['content'])) {
                        $item['content'] = call_user_func($item['content']); //闭包输出请注意xss攻击
                    } else {
                        $item['content'] = H5::encode($item['content']); //防止xss注入
                    }
                }
            }
        }

        return $options;
    }

    /**
     * 设置列表行数据
     * @param array $options
     * ```php
     * $this->setListRows([
     *      [
     *           '用户名' => $this->listRows->item('Clevstn'),
     *           '积分' => $this->listRows->item(500),
     *           '状态' => $this->listRows->item(function () {
     *                  return H5::badge('成功');
     *           }),
     *      ],
     *      '手机号' => $this->listRows->item(function () {
     *              return '15030321254';
     *       })->tdAttribute(['colspan' => 5]),
     *      '订单号' => $this->listRows->item('N01878016587493544427')->tdAttribute(['colspan' => 5]),
     * ]);
     * ```
     * @return $this
     */
    public function setListRows(array $options)
    {
        $this->_listRows = $this->_listRowsRecursiveMap($options);
        return $this;
    }

    /**
     * @return array
     */
    public function getListRows()
    {
        return $this->_listRows;
    }

    /**
     * 设置额外的JS
     * @param array|string $js 额外的JS脚本
     * @return $this
     */
    public function setExtraJs($js)
    {
        $js = (array)$js;
        foreach ($js as &$value) {
            if (is_callable($value)) {
                $value = call_user_func($value);
            }
        }

        $this->_extraJs = array_merge($this->_extraJs, $js);
        return $this;
    }

    /**
     * 获取额外的JS
     * @return array
     */
    public function getExtraJs()
    {
        return $this->_extraJs;
    }

    /**
     * 设置额外的CSS
     * @param array|string $css 额外的CSS脚本
     * @return $this
     */
    public function setExtraCss($css)
    {
        $css = (array)$css;
        foreach ($css as &$value) {
            if (is_callable($value)) {
                $value = call_user_func($value);
            }
        }

        $this->_extraCss = array_merge($this->_extraCss, $css);
        return $this;
    }

    /**
     * 获取额外的CSS
     * @return array
     */
    public function getExtraCss()
    {
        return $this->_extraCss;
    }

    /**
     * 设置额外的静态资源包
     * @param string|array $extraAssetBundle 静态资源包
     * @return $this
     */
    public function setExtraAssetBundle($extraAssetBundle)
    {
        $extraAssetBundle = (array)$extraAssetBundle;

        // 检查
        foreach ($extraAssetBundle as $assetBundle) {
            $tempObj = new $assetBundle;
            if (!$tempObj instanceof AssetBundle) {
                throw new InvalidArgumentException($assetBundle . ' is not instance of ' . AssetBundle::class);
            }
        }

        $this->_extraAssetBundle = array_merge($this->_extraAssetBundle, $extraAssetBundle);
        return $this;
    }

    /**
     * 获取额外的静态资源包
     * @return array
     */
    public function getExtraAssetBundle()
    {
        return $this->_extraAssetBundle;
    }

    /**
     * 构建器名称
     * @return string
     */
    public function getName()
    {
        return 'list';
    }

    /**
     * @return string the view path that may be prefixed to a relative view name.
     */
    public function getViewPath()
    {
        return __DIR__;
    }

    /**
     * 列表渲染
     * @param boolean $onlyRenderContent 是否仅渲染内容
     * @return string
     * @throws \yii\base\InvalidConfigException
     */
    public function render($onlyRenderContent = false)
    {
        if ($this->title) {
            // 注册页面TITLE
            $this->_view->title = H5::encode(App::params('admin.brandName') . ' | ' . $this->title);
        }

        // 注册JS脚本
        $this->_view->registerJs($this->_resolveJsScript(), View::POS_END);

        // 注册额外CSS脚本
        if (!empty($this->extraCss)) {
            $this->_view->registerCss(implode("\n", $this->extraCss));
        }

        // 注册额外的AssetBundle
        if (!empty($this->extraAssetBundle)) {
            foreach ($this->extraAssetBundle as $assetBundle) {
                $this->_view->registerAssetBundle($assetBundle);
            }
        }

        // 渲染表单内容
        $content = $this->_view->render('view', [
            // view.php中参数注册
            'rootLayout' => $this->rootLayout,      // 根布局
            'listLayout' => $this->listLayout,      // 列表布局
            'listRows' => $this->listRows,          // 列表行数据

        ], $this);

        // 仅内容渲染
        if ($onlyRenderContent) {
            return $content;
        }

        // 局部布局渲染(不包含: 菜单和导航栏)
        if ($this->partial) {
            return $this->_view->renderFile($this->partialPath, ['content' => $content], $this);
        }

        // 主布局渲染(包含: 菜单和导航栏)
        return $this->_view->renderFile($this->mainPath, ['content' => $content], $this);
    }

    /**
     * 解析JS脚本
     * @return string
     */
    private function _resolveJsScript()
    {
        $jsScripts = $this->_view->render('app', [
            // app.php中参数注册
            'extraJs' => $this->extraJs, //额外的JS脚本

        ], $this);

        return RegEx::js($jsScripts);
    }
}